package gamelogic

import (
	"encoding/json"
	"server_cluster/common/mysock"
	"server_cluster/logic/game_config"
	"slices"
	"sort"

	"server_cluster/common/config"
	"server_cluster/common/gdconf"
	"server_cluster/common/logger"
	"server_cluster/common/protoc/pb"
	"server_cluster/common/sharedb"
	"server_cluster/http/pkg/random"
	"server_cluster/logic/db"
	"server_cluster/logic/sharemem"
)

func CreateBattle(user *sharemem.User, buf *mysock.SockBuf, BattleGroupId int32, IsReset, ActiveFight, isGmFight bool) int16 {
	trailGroupId := user.GetTrailGroupInfoBySceneId(user.SceneId)
	if trailGroupId == 0 {
		// todo:以battleGroupID来判断是否有试用角色
	}
	if !(isGmFight && game_config.GetConfig().DevEnv) {
		ret := SceneFightStart(BattleGroupId, user)
		if ret != config.OK {
			logger.Error("SceneFightStart error, ret: %v, uid: %v", ret, user.GetUid())
			return config.OK
		}
	}
	// 打包角色战斗数据
	pbFightData := sharemem.PacketPbFightData(user, BattleGroupId, ActiveFight, trailGroupId)
	if pbFightData == nil {
		logger.Error("PacketPbFightData error, uid: %v", user.GetUid())
		return config.OK
	}
	data, _ := json.Marshal(pbFightData)
	logger.Debug("[CreateBattle] data: %v", string(data))

	// 选择战斗服务器
	serverList, err := new(sharedb.Config_server).GetAllOnlineFightServer(db.Conn)
	if err != nil {
		logger.Error("query battle server error: %v, uid: %v", err, user.GetUid())
		return config.OK
	}
	serverList = slices.DeleteFunc[[]*sharedb.Config_server, *sharedb.Config_server](serverList, func(server *sharedb.Config_server) bool {
		return server.Stock == 0
	})
	if len(serverList) == 0 {
		logger.Error("no battle server found")
		return config.OK
	}
	sort.Slice(serverList, func(i, j int) bool {
		return serverList[i].Stock > serverList[j].Stock
	})
	maxStock := serverList[0].Stock
	serverList = slices.DeleteFunc[[]*sharedb.Config_server, *sharedb.Config_server](serverList, func(server *sharedb.Config_server) bool {
		return server.Stock != maxStock
	})
	rn := random.GetRandomInt32(0, int32(len(serverList)-1))
	server := serverList[rn]

	SendMsg(config.SC_CREATE_BATTLE, &pb.SS_Create_Battle{
		FightServerId: int32(server.Id),
		ServerData:    pbFightData,
		IsReset:       IsReset,
		ActiveFight:   ActiveFight,
	}, buf, user)
	return config.OK
}

func handleBattleReward(user *sharemem.User, battleGroupId int32, firstKill bool, teamList []int32) (int32, []*pb.Item, int32, int32) {
	battleGroupConfig := gdconf.GetBattleGroupConfig(battleGroupId)
	if battleGroupConfig == nil {
		return config.CONFIG_NOT_FOUND, nil, 0, 0
	}
	itemMap := make(map[int32]int32)
	// 普通奖励
	var totalRoleAddExp int32 = 0
	for _, reward := range battleGroupConfig.RewardList {
		ret, itemMapOnce, RoleExp := RewardItem(reward, teamList, user)
		if ret != config.OK {
			logger.Error("reward item error, rewardId: %v, ret: %v, uid: %v", reward, ret, user.GetUid())
			continue
		}
		for k, v := range itemMapOnce {
			itemMap[k] += v
		}
		totalRoleAddExp += RoleExp
	}
	// 首通奖励
	if firstKill {
		for _, reward := range battleGroupConfig.OnceRewardList {
			ret, itemMapOnce, roleExp := RewardItem(reward, teamList, user)
			if ret != config.OK {
				logger.Error("reward item error, rewardId: %v, ret: %v, uid: %v", reward, ret, user.GetUid())
				continue
			}
			for k, v := range itemMapOnce {
				itemMap[k] += v
			}
			totalRoleAddExp += roleExp
		}
	}
	// 额外奖励
	ret, extItemMap, roleExp := handleBattleExtReward(user, battleGroupConfig, teamList)
	if ret != config.OK {
		logger.Error("ext reward item error, cond: %v, ret: %v, uid: %v", battleGroupConfig.ExtRewardCond, ret, user.GetUid())
	}
	roleExp += totalRoleAddExp
	for k, v := range extItemMap {
		itemMap[k] += v
	}
	var userAddExp int32 = 0
	pbItemList := make([]*pb.Item, 0)
	for k, v := range itemMap {
		pbItemList = append(pbItemList, &pb.Item{Id: k, Num: v})
		if k == gdconf.ITEM_EXP {
			userAddExp += v
		}
	}
	AddExp4RoleList(roleExp, teamList, user)
	return config.OK, pbItemList, roleExp, userAddExp
}

func handleBattleExtReward(user *sharemem.User, battleGroupConfig *gdconf.BattleGroupConfig, teamList []int32) (int32, map[int32]int32, int32) {
	if battleGroupConfig.ExtRewardCond == nil {
		return config.OK, nil, 0
	}
	switch battleGroupConfig.ExtRewardCond[0] {
	case gdconf.BattleGroupExtRewardCondTypeCurrQuest:
		questId := battleGroupConfig.ExtRewardCond[1]
		questConfig := gdconf.GetConfigQuest(questId)
		subQuest := user.GetSubQuest(questConfig.ParentQuestId, questId)
		if subQuest == nil {
			return config.OK, nil, 0
		}
		if subQuest.State == config.QUEST_STATE_FIN {
			return config.OK, nil, 0
		}
		ret, itemMap, roleExp := RewardItem(battleGroupConfig.ExtRewardCond[2], teamList, user)
		if ret != config.OK {
			logger.Error("reward item error, rewardId: %v, ret: %v, uid: %v", battleGroupConfig.ExtRewardCond[2], ret, user.GetUid())
		}
		return config.OK, itemMap, roleExp
	default:
		return config.OK, nil, 0
	}
}

// SceneFightStart 进入场景战斗
func SceneFightStart(battleGroupId int32, user *sharemem.User) int32 {
	logger.Info("[MarkPlayerAction SceneFightStart %v %v]", battleGroupId, user.GetUid())
	logger.Debug("[SceneFightStart] battleGroupId: %v, uid: %v", battleGroupId, user.GetUid())
	battleGroupConfig := gdconf.GetBattleGroupConfig(battleGroupId)
	if battleGroupConfig == nil {
		return config.CONFIG_NOT_FOUND
	}
	for _, fightId := range battleGroupConfig.FightIdList {
		battleConfig := gdconf.GetBattleConfig(fightId)
		if battleConfig == nil {
			continue
		}
		for _, battleId := range battleConfig.MonsterIdList {
			monsterConfig := gdconf.GetBattleMonsterConfig(battleId)
			if monsterConfig == nil {
				continue
			}
			UnlockCodex(pb.CodexType_CODEX_MONSTER, monsterConfig.MonsterCodexType, user)
		}
	}
	switch battleGroupConfig.Type {
	case gdconf.BattleGroupTypeSceneMonster:
		var ret = user.CheckBattleGroupTypeSceneMonster(battleGroupId)
		if ret != config.OK {
			return ret
		}
	case gdconf.BattleGroupTypeDungeon:
	case gdconf.BattleGroupTypeTower:
	default:
	}
	return config.OK
}

//======消息协议处理=====

func CreateBattleReq(user *sharemem.User, buf *mysock.SockBuf) int16 {
	req, ok := ParseMsg(config.CS_CREATE_BATTLE, new(pb.CS_Create_Battle), buf, user).(*pb.CS_Create_Battle)
	if !ok {
		return config.PROTO_ERROR
	}

	if req.GmFight && !game_config.GetConfig().DevEnv && !user.IsGmUser {
		logger.Error("user can not gm fight, uid: %v", user.GetUid())
		return config.OK
	}
	return CreateBattle(user, buf, req.BattleGroupId, req.IsReset, req.ActiveFight, req.GmFight)
}

func BattleOver(user *sharemem.User, buf *mysock.SockBuf) int16 {
	req, ok := ParseMsg(config.SC_BATTLE_OVER, new(pb.SS_Battle_Over), buf, user).(*pb.SS_Battle_Over)
	if !ok {
		return config.PROTO_ERROR
	}

	var pTeam = user.GetTeam(user.GetActiveTeamId())
	var teamList = pTeam.GetActiveTeamListAll()
	trailGroupId := user.GetTrailGroupInfoBySceneId(user.SceneId)
	var trailRoleConfigs = make(map[int32]*gdconf.TrailRoleConfig)
	if trailGroupId != 0 {
		trailRoleConfigs = gdconf.GetTrailRoleConfigMap4Pos(trailGroupId)
		if trailRoleConfigs == nil {
			logger.Error("trailRoleConfigMap4Pos error, trailGroupId: %v", trailGroupId)
			return config.OK
		}
	}
	roleExpLevel := GetRoleExpLevel4Role(&teamList, user, trailRoleConfigs)

	var lastUserExpLevel = &pb.RoleExpLevel{RoleLevel: int32(user.Level), RoleExp: user.Exp}

	battleGroupId := req.Battle.BattleGroupId
	if !req.Status {
		SendMsg(config.SC_BATTLE_OVER, &pb.SC_Battle_Over{
			BattleGroupId: battleGroupId,
			ItemList:      nil,
			Status:        false,
			ActiveFight:   req.ActiveFight,
			RoleExpLevel:  roleExpLevel,
			UserExpLevel:  lastUserExpLevel,
		}, buf, user)
		return config.OK
	}
	// 回复冲撞次数
	user.UserScene.CurrCollCount += int32(gdconf.GetConstInt("COLLISIONSREPLY"))
	if user.UserScene.CurrCollCount >= int32(gdconf.GetConstInt("AVSMAXCOLLISIONS")) {
		user.UserScene.CurrCollCount = int32(gdconf.GetConstInt("AVSMAXCOLLISIONS"))
	}
	SendMsg(config.SC_SceneDashUpdateNtf, &pb.SC_SceneDashUpdateNtf{CurrCollCount: int64(user.UserScene.CurrCollCount)}, buf, user)

	ret, firstKill := SceneFightFinish(battleGroupId, user)
	if ret != config.OK {
		logger.Error("SceneFightFinish error, ret: %v, uid: %v", ret, user.GetUid())
		return config.OK
	}
	// 发放奖励
	ret, pbItemList, roleAddExp, userAddExp := handleBattleReward(user, battleGroupId, firstKill, teamList)
	if ret != config.OK {
		logger.Error("BattleReward error, ret: %v, uid: %v", ret, user.GetUid())
		return config.OK
	}

	// 保存战斗角色当前值属性
	activeTeamId := user.GetActiveTeamId()
	team := user.GetTeam(activeTeamId)
	if team == nil {
		logger.Error("no active team, activeTeamId: %v, uid: %v", activeTeamId, user.GetUid())
		return config.OK
	}
	//trailGroupId := int(user.GetTrailGroupInfoBySceneId(user.SceneId))
	//if trailGroupId == 0 {
	//	// todo:以battleGroupID来判断是否有试用角色
	//}
	for k, fightRole := range req.Battle.FightRole {
		if fightRole.Site < 0 || fightRole.Site > 3 {
			logger.Error("fight role index error, index: %v, uid: %v", fightRole.Site, user.GetUid())
			return config.OK
		}
		if trailRoleConfigs[int32(k)] != nil {
			continue
		}
		roleId := team.FightRoleList[fightRole.Site]
		role := user.GetRoleById(roleId)
		if role == nil {
			logger.Error("get role is nil, roleId: %v, uid: %v", roleId, user.GetUid())
			return config.OK
		}
		for _, attr := range fightRole.Attr {
			if attr.Id == config.FIGHT_PROP_CUR_ENERGY {
				role.CurrEnergy = attr.Val
				role.SetSync()
			}
			if attr.Id == config.FIGHT_PROP_CUR_HP {
				role.CurrHp = attr.Val
				role.SetSync()
			}
		}
	}
	SendMsg(config.SC_BATTLE_OVER, &pb.SC_Battle_Over{
		BattleGroupId: battleGroupId,
		ItemList:      pbItemList,
		Status:        true,
		ActiveFight:   req.ActiveFight,
		RoleAddExp:    roleAddExp,
		UserAddExp:    userAddExp,
		RoleExpLevel:  roleExpLevel,
		UserExpLevel:  lastUserExpLevel,
	}, buf, user)
	return config.OK
}

func GetRoleExpLevel4Role(roleList *[]int32, user *sharemem.User, trailRoleConfigs map[int32]*gdconf.TrailRoleConfig) []*pb.RoleExpLevel {
	if roleList == nil {
		return nil
	}
	var roleExpLevel = make([]*pb.RoleExpLevel, 0)
	for k, v := range *roleList {
		var exp int32 = 0
		var level int16 = 0
		var id int32 = 0
		var isTrail = false
		var rolePromote int32 = 0
		data, isExist := trailRoleConfigs[int32(k)]
		if isExist {
			level = int16(data.Level)
			id = data.RoleId
			(*roleList)[k] = 0
			isTrail = true
			rolePromote = 0
		} else {
			var role = user.GetRoleById(v)
			if role != nil {
				exp = role.Exp
				level = role.Level
				id = v
				rolePromote = int32(role.Promote)
			}
		}
		roleExpLevel = append(roleExpLevel, &pb.RoleExpLevel{IsTrail: isTrail, RolePromote: rolePromote, RoleId: id, RoleExp: exp, RoleLevel: int32(level)})
	}
	return roleExpLevel
}
